﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 凯蒽斯.Help
{
    public static class PlcTypeHelp
    {
        /// <summary>
        /// 根据PLC类型将ushort数组转换为指定类型数组（Int64[] 或 double[]）
        /// </summary>
        /// <typeparam name="TResult">目标数组类型（必须是Int64或double）</typeparam>
        /// <param name="sourceArray">Modbus读取的ushort寄存器数组</param>
        /// <param name="plcType">PLC数据类型标识（0-3、6-7返回Int64[]；4-5返回double[]）</param>
        /// <returns>转换后的数组（Int64[] 或 double[]）</returns>
        /// <exception cref="ArgumentNullException">源数组为null</exception>
        /// <exception cref="NotSupportedException">不支持的plcType或目标类型</exception>
        public static TResult[] ConvertTo<TResult>(ushort[] sourceArray, int plcType)
        {
            if (sourceArray == null)
                throw new ArgumentNullException(nameof(sourceArray), "源ushort数组不能为null");

            // 检查目标类型是否为支持的类型（Int64或double）
            Type targetType = typeof(TResult);
            if (targetType != typeof(Int64) && targetType != typeof(double))
                throw new NotSupportedException($"不支持的目标类型：{targetType.Name}，仅支持Int64和double");

            // 根据plcType和目标类型转换
            if (targetType == typeof(Int64))
            {
                return ConvertToInt64Array(sourceArray, plcType) as TResult[];
            }
            else // targetType == typeof(double)
            {
                return ConvertToDoubleArray(sourceArray, plcType,false) as TResult[];
            }
        }

        // 转换为Int64数组（处理plcType 0-3、6-7）
        private static Int64[] ConvertToInt64Array(ushort[] sourceArray, int plcType)
        {
            switch (plcType)
            {
                case 0:
                    return ParseByteToInt64(sourceArray);
                case 1:
                    return ParseByteToInt64(sourceArray); ;
                case 2:
                    return Array.ConvertAll(sourceArray, ushortValue => (Int64)(uint)ushortValue);
                case 6:
                    return Array.ConvertAll(sourceArray, ushortValue => (Int64)(uint)ushortValue);
                case 3:
                    return UshortToIntArrayHalfLength(sourceArray,false);
                case 7:
                    return UshortToIntArrayHalfLength(sourceArray, true);
                default:
                    throw new NotSupportedException($"不支持的plcType {plcType} 转换为Int64数组");
            }
        }

        // 转换为double数组（处理plcType 4-5）
        private static double[] ConvertToDoubleArray(ushort[] sourceArray, int plcType, bool isBigEndian)
        {
            // 基础校验：源数组不能为null
            if (sourceArray == null)
                throw new ArgumentNullException(nameof(sourceArray), "源数组不能为null");

            switch (plcType)
            {
                case 4: // REAL（32位单精度浮点）：每2个ushort拼接成1个32位数据
                        // 校验：数组长度必须是2的倍数（否则无法成对拼接）
                    if (sourceArray.Length % 2 != 0)
                        throw new ArgumentException("REAL类型要求源数组长度为2的倍数", nameof(sourceArray));

                    int realCount = sourceArray.Length / 2;
                    double[] realResults = new double[realCount];

                    for (int i = 0; i < realCount; i++)
                    {
                        // 取当前组的2个ushort（索引：i*2 和 i*2+1）
                        ushort highOrLow1 = sourceArray[i * 2];
                        ushort highOrLow2 = sourceArray[i * 2 + 1];

                        // 根据字节序拼接为32位整数（uint）
                        uint combined32 = isBigEndian
                            ? (uint)((highOrLow1 << 16) | highOrLow2)  // 大端：前为高16位，后为低16位
                            : (uint)((highOrLow2 << 16) | highOrLow1); // 小端：前为低16位，后为高16位

                        // 转换为32位float，再转double
                        byte[] floatBytes = BitConverter.GetBytes(combined32);
                        realResults[i] = (double)BitConverter.ToSingle(floatBytes, 0);
                    }
                    return realResults;

                case 5: // LREAL（64位双精度浮点）：每4个ushort拼接成1个64位数据
                        // 校验：数组长度必须是4的倍数（否则无法4个一组拼接）
                    if (sourceArray.Length % 4 != 0)
                        throw new ArgumentException("LREAL类型要求源数组长度为4的倍数", nameof(sourceArray));

                    int lrealCount = sourceArray.Length / 4;
                    double[] lrealResults = new double[lrealCount];

                    for (int i = 0; i < lrealCount; i++)
                    {
                        // 取当前组的4个ushort（索引：i*4 到 i*4+3）
                        ushort p1 = sourceArray[i * 4];
                        ushort p2 = sourceArray[i * 4 + 1];
                        ushort p3 = sourceArray[i * 4 + 2];
                        ushort p4 = sourceArray[i * 4 + 3];

                        // 根据字节序拼接为64位整数（ulong）
                        ulong combined64 = isBigEndian
                            ? // 大端：从左到右依次为高到低16位（p1最高，p4最低）
                              (ulong)p1 << 48 | (ulong)p2 << 32 | (ulong)p3 << 16 | p4
                            : // 小端：从左到右依次为低到高16位（p1最低，p4最高）
                              (ulong)p4 << 48 | (ulong)p3 << 32 | (ulong)p2 << 16 | p1;

                        // 直接转换为64位double
                        byte[] doubleBytes = BitConverter.GetBytes(combined64);
                        lrealResults[i] = BitConverter.ToDouble(doubleBytes, 0);
                    }
                    return lrealResults;

                default:
                    throw new NotSupportedException($"不支持的plcType {plcType}（支持4=REAL,5=LREAL）");
            }
        }
        /// <summary>
        /// 针对PLC类型1（BYTE数组）的转换
        /// </summary>
        private static Int64[] ParseByteToInt64(ushort[] sourceArray)
        {
            // 步骤1：ushort[] → byte[]（长度为sourceArray的2倍）
            byte[] byteArray = new byte[sourceArray.Length * 2];
            for (int i = 0; i < sourceArray.Length; i++)
            {
                ushort value = sourceArray[i];
                byteArray[i * 2] = (byte)(value & 0xFF); // 低8位（第1个byte）
                byteArray[i * 2 + 1] = (byte)(value >> 8); // 高8位（第2个byte）
            }

            // 步骤2：byte[] → Int64[]（长度与byteArray一致）
            Int64[] result = new Int64[byteArray.Length];
            for (int i = 0; i < byteArray.Length; i++)
            {
                result[i] = (Int64)byteArray[i]; // 每个byte直接转为Int64
            }

            return result;
        }
        /// <summary>
        /// 针对PLC类型WORD（BYTE数组）的转换
        /// </summary>
        public static Int64[] UshortToIntArrayHalfLength(ushort[] sourceArray, bool isBigEndian = true)
        {
            if (sourceArray == null)
                throw new ArgumentNullException(nameof(sourceArray), "源ushort数组不能为null");

            // 计算目标数组长度（整数除法，自动忽略奇数长度的最后一个元素）
            int targetLength = sourceArray.Length / 2;
            Int64[] result = new Int64[targetLength];

            for (int i = 0; i < targetLength; i++)
            {
                // 取当前位置的2个ushort（索引：2i和2i+1）
                ushort first = sourceArray[i * 2];
                ushort second = sourceArray[i * 2 + 1];

                if (isBigEndian)
                {
                    // 大端序：第一个ushort是高位，第二个是低位 → (high << 16) | low
                    result[i] = (Int64)((first << 16) | second);
                }
                else
                {
                    // 小端序：第一个ushort是低位，第二个是高位 → (high << 16) | low
                    result[i] = (Int64)((second << 16) | first);
                }
            }

            return result;
        }
    }
}
